package com.cyberstep.onstage.audio;

import com.cyberstep.onstage.util.Sys;
import java.io.*;
import java.util.Hashtable;
import java.util.Vector;
import javax.sound.sampled.*;

// Referenced classes of package com.cyberstep.onstage.audio:
//            Audio, SoundResource, Sound

public class JSAudio
    implements Audio
{
    class JSSoundList
    {

        public void makeChannel(int numchannel)
        {
            try
            {
                for(int l = soundlist.size(); l < numchannel; l++)
                {
                    JSSound newsound = new JSSound(resource);
                    soundlist.addElement(newsound);
                }

            }
            catch(Exception e)
            {
                System.err.println("JSAudio.JSSoundList.makeChannel failed.");
                e.printStackTrace();
            }
        }

        public Sound getChannel()
        {
            if(soundlist.size() == 0)
                makeChannel(1);
            if(soundlist.size() == 0)
                return null;
            for(int l = 0; l < soundlist.size(); l++)
                if(!((JSSound)soundlist.elementAt(l)).isPlaying())
                    return (JSSound)soundlist.elementAt(l);

            JSSound c = (JSSound)soundlist.elementAt(0);
            long ttime = c.starttime;
            for(int l = 1; l < soundlist.size(); l++)
            {
                JSSound jss = (JSSound)soundlist.elementAt(l);
                if(ttime < jss.starttime)
                {
                    c = jss;
                    ttime = jss.starttime;
                }
            }

            return c;
        }

        SoundResource resource;
        Vector soundlist;

        public JSSoundList(SoundResource res)
        {
            soundlist = new Vector();
            resource = res;
        }
    }

    class JSSound
        implements Sound
    {

        public void play()
        {
            try
            {
                clip.setFramePosition(0);
            }
            catch(AbstractMethodError e)
            {
                System.err.println("AbstractMethodError catched!");
                e.printStackTrace();
            }
            clip.start();
            starttime = Sys.getTime();
        }

        public void play(int lc)
        {
            if(lc == 1)
            {
                play();
            } else
            {
                clip.setFramePosition(0);
                clip.setLoopPoints(0, 20000);
                clip.loop(lc != 0 ? lc - 1 : -1);
            }
            starttime = Sys.getTime();
        }

        public void play(float sp, float lp, int lc)
        {
            int sf = (int)(basefreq * sp);
            int ef = (int)(basefreq * lp);
            try
            {
                clip.setFramePosition(0);
                clip.setLoopPoints(sf, ef);
                clip.loop(lc);
            }
            catch(Exception exception) { }
            starttime = Sys.getTime();
        }

        public void stop()
        {
            clip.stop();
        }

        public void setPitch(float t)
        {
            pitch = t;
            if(pitchctrl!=null)
            pitchctrl.setValue(basefreq * t);
        }

        public void setPan(float p)
        {
            pan = p;
            if(panctrl!=null)
            	panctrl.setValue(p);
        }

        public void setVolume(float v)
        {
            vol = v;
            v = Math.max(v, 1.0F);
            v = Math.min(v, 100F);
            v *= mastervol;
            volctrl.setValue(20F * (float)(Math.log(v * 0.01F) / JSAudio.loge10));
        }

        public float getPitch()
        {
            return pitch;
        }

        public float getPan()
        {
            return pan;
        }

        public float getVolume()
        {
            return vol;
        }

        public boolean isPlaying()
        {
            return clip.isRunning();
        }

        public void close()
        {
            clip.flush();
            clip.close();
        }

        Clip clip;
        FloatControl volctrl;
        FloatControl panctrl = null;
        FloatControl pitchctrl = null;
        float basefreq;
        float vol;
        float voloffs;
        float volrange;
        float pan;
        float pitch;
        long starttime;

        JSSound(SoundResource res)
            throws Exception
        {
            javax.sound.sampled.Line.Info info = new javax.sound.sampled.Line.Info(javax.sound.sampled.Clip.class);
            if(!AudioSystem.isLineSupported(info))
            {
                System.err.println("line not supported");
                throw new Exception();
            } else
            {
                AudioFormat af = new AudioFormat(res.freq, res.samplebits, res.channel, res.issigned, res.isbigendian);
                clip = (Clip)AudioSystem.getLine(info);
                clip.open(af, res.data, 0, res.data.length);
                basefreq = res.freq;
                volctrl = (FloatControl)clip.getControl(javax.sound.sampled.FloatControl.Type.MASTER_GAIN);
                try{
                	panctrl = (FloatControl)clip.getControl(javax.sound.sampled.FloatControl.Type.PAN);
                }catch(IllegalArgumentException e){}
                try{
                	pitchctrl = (FloatControl)clip.getControl(javax.sound.sampled.FloatControl.Type.SAMPLE_RATE);
                }catch(IllegalArgumentException e){}
                return;
            }
        }
    }


    public JSAudio()
    {
        soundtable = new Hashtable(100);
        mastervol = 1.0F;
        try
        {
            setCondition(0);
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
    }

    public String[] getConditions()
    {
        return conditions;
    }

    public void setCondition(int n)
    {
        mixer = AudioSystem.getMixer(mixers[n]);
        if(mixer.isControlSupported(javax.sound.sampled.FloatControl.Type.VOLUME))
            masterctrl = (FloatControl)mixer.getControl(javax.sound.sampled.FloatControl.Type.VOLUME);
    }

    public void setMasterVolume(float f)
    {
        f = Math.max(0.0F, Math.min(100F, f));
        mastervol = f * 0.01F;
    }

    public float getMasterVolume()
    {
        return mastervol * 100F;
    }

    public SoundResource getSoundResource(File file)
    {
        try
        {
            SoundResource res = new SoundResource();
            AudioInputStream ais = AudioSystem.getAudioInputStream(file);
            AudioFormat af = ais.getFormat();
            System.err.println(af + " " + af.isBigEndian());
            res.freq = af.getFrameRate();
            res.samplebits = af.getSampleSizeInBits();
            res.channel = af.getChannels();
            res.data = new byte[(int)((long)(af.getSampleSizeInBits() / 8) * ais.getFrameLength() * (long)res.channel)];
            if(af.getEncoding() == javax.sound.sampled.AudioFormat.Encoding.PCM_UNSIGNED)
                res.issigned = false;
            else
                res.issigned = true;
            byte buf[] = new byte[1024];
            int readbyte = 0;
            int read = 0;
            do
            {
                read = ais.read(buf, 0, 1024);
                System.arraycopy(buf, 0, res.data, readbyte, read);
                readbyte += read;
            } while(readbyte < res.data.length && read != -1);
            ais.close();
            return res;
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
        return null;
    }

    public void preBuffer(String soundname, SoundResource res, int numchannel)
    {
        if(res != null)
        {
            Object o = soundtable.get(soundname);
            JSSoundList s = null;
            if(o != null)
                s = (JSSoundList)o;
            else
                s = new JSSoundList(res);
            soundtable.put(soundname, s);
            s.makeChannel(numchannel);
        }
    }

    public Sound getSound(String soundname, SoundResource res)
    {
        if(res == null)
            return null;
        JSSoundList s = (JSSoundList)soundtable.get(soundname);
        if(s == null)
        {
            s = new JSSoundList(res);
            soundtable.put(soundname, s);
        }
        return s.getChannel();
    }

    public static void main(String arg[])
        throws Exception
    {
        if(arg.length == 0)
        {
            System.err.println("Specify sound file name.");
            System.exit(0);
        }
        JSAudio a = new JSAudio();
        for(int i = 0; i < arg.length; i++)
        {
            SoundResource sr = a.getSoundResource(new File(arg[i]));
            Sound s = a.getSound(arg[i], sr);
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(arg[i].substring(0, arg[i].lastIndexOf(".")) + ".sr"));
            oos.writeObject(sr);
            oos.flush();
            oos.close();
            ((JSSound)s).close();
        }

        System.exit(0);
    }

    static final double loge10 = Math.log(10D);
    static javax.sound.sampled.Mixer.Info mixers[];
    static String conditions[];
    Hashtable soundtable;
    Mixer mixer;
    FloatControl masterctrl;
    float mastervol;

    static 
    {
        mixers = AudioSystem.getMixerInfo();
        conditions = new String[mixers.length];
        for(int l = 0; l < mixers.length; l++)
        {
            Mixer mixer = AudioSystem.getMixer(mixers[l]);
            conditions[l] = mixer.toString();
        }

    }
}
